package com.hc.programming.贪心;

/**
 * 如果连续数字之间的差严格地在正数和负数之间交替，则数字序列称为 摆动序列 。第一个差（如果存在的话）可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。
 * 例如， [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ，因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。
 * 相反，[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是摆动序列，第一个序列是因为它的前两个差值都是正数，第二个序列是因为它的最后一个差值为零。
 * 子序列 可以通过从原始序列中删除一些（也可以不删除）元素来获得，剩下的元素保持其原始顺序。
 * 给你一个整数数组 nums ，返回 nums 中作为 摆动序列 的 最长子序列的长度 。
 * <p>
 * 示例 1：
 * 输入：nums = [1,7,4,9,2,5]
 * 输出：6
 * 解释：整个序列均为摆动序列，各元素之间的差值为 (6, -3, 5, -7, 3) 。
 * 示例 2：
 * 输入：nums = [1,17,5,10,13,15,10,5,16,8]
 * 输出：7
 * 解释：这个序列包含几个长度为 7 摆动序列。
 * 其中一个是 [1, 17, 10, 13, 10, 16, 8] ，各元素之间的差值为 (16, -7, 3, -3, 6, -8) 。
 * 示例 3：
 * 输入：nums = [1,2,3,4,5,6,7,8,9]
 * 输出：2
 * <p>
 * 提示：
 * 1 <= nums.length <= 1000
 * 0 <= nums[i] <= 1000
 * <p>
 * 进阶：你能否用 O(n) 时间复杂度完成此题?
 *
 * @author huangchao E-mail:fengquan8866@163.com
 * @version 创建时间：2024/9/4 10:31
 */
public class 摆动序列 {
    public static void main(String[] args) {
        System.out.println("[1,7,4,9,2,5]=6,--" + wiggleMaxLength(new int[]{1, 7, 4, 9, 2, 5}));
        System.out.println("[1,17,5,10,13,15,10,5,16,8]=7,--" + wiggleMaxLength(new int[]{1, 17, 5, 10, 13, 15, 10, 5, 16, 8}));
        System.out.println("[1,2,3,4,5,6,7,8,9]=2,--" + wiggleMaxLength(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9}));
        System.out.println("[0,0]=1,--" + wiggleMaxLength(new int[]{0, 0}));
        System.out.println("[1,1,7,4,9,2,5]=6,--" + wiggleMaxLength(new int[]{1, 1, 7, 4, 9, 2, 5}));
        System.out.println("[0,1,1,7,4,9,2,5]=6,--" + wiggleMaxLength(new int[]{0, 1, 1, 7, 4, 9, 2, 5}));
        System.out.println("[1,1,1,7,4,9,2,5]=6,--" + wiggleMaxLength(new int[]{1, 1, 1, 7, 4, 9, 2, 5}));
        System.out.println("[1,1,1,7,4,4,9,2,5]=6,--" + wiggleMaxLength(new int[]{1, 1, 1, 7, 4, 4, 9, 2, 5}));
        System.out.println("[33,290,187,443,123,59,358,241,141,39,196,491,381,157,157,134,431]=10,--" + wiggleMaxLength(new int[]{33, 290, 187, 443, 123, 59, 358, 241, 141, 39, 196, 491, 381, 157, 157, 134, 431}));
    }

    public static int wiggleMaxLength(int[] nums) {
//        return 动态规划(nums);
//        return 贪心算法(nums);
        return 贪心算法2(nums);
    }

    private static int 贪心算法2(int[] nums) {
        if (nums.length == 1) return 1;
        int preDiff = 0;
        int currDiff = 0;
        int rst = 1;
        for (int i = 0; i < nums.length - 1; i++) {
            currDiff = nums[i + 1] - nums[i];
            if ((preDiff >= 0 && currDiff < 0) || (preDiff <= 0 && currDiff > 0)) {
                rst++;
                preDiff = currDiff;
            }
        }
        return rst;
    }

    private static int 贪心算法(int[] nums) {
        if (nums.length == 1) return 1;
        int sign = nums[1] - nums[0]; // 上一步的符号
        int count = 1;
        if (sign != 0) count++;
        for (int i = 2; i < nums.length; i++) {
            if (nums[i] == nums[i - 1]) {
                continue;
            }
            if (!isSameSign(sign, nums[i] - nums[i - 1])) {
                count++;
                sign = (sign == 0) ? (nums[i] - nums[i - 1]) : -sign;
            }
        }
        return count;
    }

    /**
     * dp[i]: 满足摆动序列的最大长度
     * 递归公司： dp[i] = d[i-1] + 同增 ? 0 : 1;
     * 同增：符号(dp[i] - d[i-1]) 与 符号(dp[i-1] - d[i-2]) 相同
     * dp[0]=1, dp[1]=2
     * 正逆向均可
     */
    public static int 动态规划(int[] nums) {
        if (nums.length == 1) return 1;
        int[] dp = new int[nums.length];
        dp[0] = 1;
        dp[1] = (nums[0] == nums[1]) ? 1 : 2;
        for (int i = 2; i < nums.length; i++) {
            if (nums[i] == nums[i - 1]) {
                dp[i] = dp[i - 1];
                continue;
            }
            int pre2 = i - 2;
            if (nums[i - 1] == nums[pre2]) {
                for (; pre2 > 0; pre2--) {
                    if (nums[pre2] != nums[pre2 - 1]) {
                        pre2--;
                        break;
                    }
                }
            }
            if (isSameSign(nums[i] - nums[i - 1], nums[i - 1] - nums[pre2])) {
                // 同增
                dp[i] = dp[i - 1];
            } else {
                dp[i] = dp[i - 1] + 1;
            }
        }
        return dp[nums.length - 1];
    }

    /**
     * 判断2个数字是否同号：同正 或 同负
     */
    private static boolean isSameSign(int n1, int n2) {
        return (n1 > 0 && n2 > 0) || (n1 < 0 && n2 < 0);
    }
}
